// 第一题
// 对象的遍历总结

// 1、对象常用遍历方式
    // for...in
    // Object.keys()
    // Object.getOwnPropertyNames()
    // Object.getOwnPropertySymbols()
    // Reflect.ownKeys()


// 2、对象的属性分类
    // 原型属性
    // 对象自身的可以枚举属性
    // 对象自身的不可枚举属性
    // symbol属性

// 注意: Object.create是利用给定的对象作为原型对象进行创建新的对象
const obj = Object.create({
    bar: 'bar'// 原型属性
});
obj.foo = 'foo';// 对象的自身可枚举属性
Object.defineProperty(obj, 'name', {// 对象自身不可枚举的属性
    enumerable: false,
    value: 'kongs'
});
obj[Symbol('age')] = 'age'; // 对象的symbol属性

// 关于Symbol属性的拓展
// 1、let a = Symbol('a'), 创建symbol实例是不能用new创建的
// 2、具有相同描述的symbol实例是不相等的, Symbpl('b') != Symbol('b')
// 3、如果想得到同一个描述的相等的两个symbol,可以这样:Symbol.for('c') == Symbol.for('c')


// 3、for...in循环
// 该方法是可以获取对象自身的可枚举属性 + 原型链上的可以枚举属性,但是不含symbol属性

// 3.1
for(let key in obj) {
    console.log(key)
};
// 结果会输出: bar foo



// 3.2 如果想过滤掉原型上的属性可以这样:
for(let key in obj) {
    if (obj.hasOwnProperty(key)) {
        console.log(key)
    }
};
// 结果会输出:  foo

// 3.3 for..in的输出顺序:
// 对于大于等于0的整数,会按照大小进行排序, 对于小数和负数就会当作字符串处理
// 对于string类型,按照第一的顺序进行输出
// symbol属性将被过滤掉,不会输出

const obj2 = {
    5: '5',
    a: 'a',
    1: '1',
    c: 'c',
    '-1': '-1',
    '3': '3',
    b: 'b'
}
for(let key in obj2) {
    console.log(key);
}

// 结果会输出:  1 3 5  a c -1 b


// 4、Object.keys()方法
// 该方法获取对象自身的可枚举属性
// 不包含原型上的可枚举属性和symbol属性
// es5新增

Object.keys(obj).forEach(key =>  console.log(key)) // 输出: foo



// 5、Object.getOwnPropertyNames()方法
// 该方法获取对象自身的所有属性,包括不可枚举属性
// 但是不包含原型属性和symbol属性
// es5新增
Object.getOwnPropertyNames(obj).forEach(i => console.log(i))// 输出: foo name

// 拓展:如何判断属性是否可以枚举?
//方法一:
 Object.getOwnPropertyDescriptor(obj, 'name')
//方法二:
Object.prototype.propertyIsEnumerable.call(obj, ['name'])


// 6、Object.getOwnPropertySymbols()方法
// 该方法获取对象自身所有的symbol属性
// 不包括原型上的symbol属性
// es6新增

Object.getOwnPropertySymbols(obj).forEach(i => console.log(i));// 输出: Symbol(age)


// 7.Reflect.ownKeys()方法
//该方法获取对象自身的所有属性,包括不可枚举和symbol属性
// es6中新增

Reflect.ownKeys(obj).forEach(i => console.log(i)); // 输出: foo name Symbol(age)